/* Name: Nebula
 * Author: Cameron Hoerig
 * Date: 5-13-12
 * Description: The Nebula class creates and draws nebulae on screen. Players can create 
 *  wormholes to Nebula, but not through them.
 * Revisions: See Subversion logs at http://code.google.com/p/software-engineering-2012/source/list
 */

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import java.util.LinkedList;

public class Nebula extends Item {
	private static LinkedList<Nebula> idList = new LinkedList<Nebula>();
	private Sprite sprite; // holds the sprite for the object
	private float scale; // scale of the sprite
	private float minScale; // min scale of the sprite
	private float maxScale; // max scale of the sprite
	private int SPRITE_RADIUS;
	private float scaleSpeed;
	private Boolean scaleIncreasing;
	private float rot = 0;
	private float rotSpeed = 0;
	private LinkedList<Wormhole> whList = new LinkedList<Wormhole>();
	private float fuelPushRate = 0;
	
	// Places the Nebula randomly in the room, preventing collisions and impossible scenarios. Initializes graphics.
	public Nebula(int px, int py) {
		Game.create((Item)this, 15); // Needed for update, draw, and destroy
		colType = COL_CIRCLE; // Needed for collision checking
		idList.add(this); // Needed for collision checking
		String tmpStr = String.format("/sprites/Nebula/nebula%d.png", Game.randomInt(3)+1);
		sprite = Sprite.load(tmpStr); // load the sprite
		SPRITE_RADIUS = 134; 
		if (px == -1) {
			int attempt = 0;
			do {
				r = Game.randomInt(120, 164)*2;
				x = Game.randomInt(r,GameScreen.getWidth()-r); // randomly place the object
				y = Game.randomInt(r,GameScreen.getHeight()-r);
			} while (Planet.checkCollision(this, false)!=0 || Nebula.checkCollision(this, false)!=0 || 
					AsteroidField.checkCollision(this, false)!=0 || Antimatter.checkCollision(this, false)!=0 ||
					Wormhole.checkCollision(this, false) != 0 || !GameScreen.isGameWinnable() || ++attempt >= 30);
			if (attempt >= 30) {
				GameScreen.throwRoomCreationError();
			}
		} else {
			r = Game.randomInt(120, 164)*2;
			x = px;
			y = py;
		}
		r /= 2; // set the radius 
		maxScale = (float)(r)/SPRITE_RADIUS;
		minScale = maxScale * 0.85f;
		scale = Game.randomFloat(minScale, maxScale);
		scaleIncreasing = (Game.randomInt(2)==0);
		scaleSpeed = Game.randomFloat(0.0001f, 0.0005f);
		rot = Game.randomFloat((float) (2.0f*Math.PI));
		if (Game.randomInt(2) == 0) {
			rotSpeed = (float) (Game.randomFloat((float) (2.0f*Math.PI/10000.0f), (float) (2.0f*Math.PI/5000.0f)));
		} else {
			rotSpeed = (float) (Game.randomFloat((float) (-2.0f*Math.PI/5000.0f), (float) (-2.0f*Math.PI/10000.0f)));
		}
		r = (int) (SPRITE_RADIUS*scale); // set the radius 
		fuelPushRate = Game.randomFloat(1f,2.5f);
		active = true;
	}
	
	// Called every frame: Updates the sprite positions for drawing
	public void update() {
		// Draw
		rot += rotSpeed;
		if (scaleIncreasing) {
			scale += scaleSpeed;
			if (scale >= maxScale) {
				scaleIncreasing = false;
			}
		} else {
			scale -= scaleSpeed;
			if (scale <= minScale) {
				scaleIncreasing = true;
			}
		}
	}
	
	// Sets any colliding Wormholes to invalid unless mouse within self
	public void collision(Item other) {
		if (other instanceof Wormhole){
			Wormhole cOther = (Wormhole)other;
			if(cOther.x != this.x & cOther.y != this.y & Math.sqrt(Math.pow(cOther.x2-this.x,2)+Math.pow(cOther.y2-this.y,2)) > r){ // if the starting end of the wormhole is not connected
				cOther.setPlacementValidity(false);		 																			// to this planet
			}
		}
	}
	
	// Called when destroyed: removes self from Nebula list
	public void destroy() {
		idList.remove(this); // Needed for collision checking
		
	}
	
	// Called every frame: Draws the Nebula sprite and resource rate
	public void draw() {
		Graphics2D gh = Game.getGraphicsHandle();		
		sprite.draw(gh, x, y, rot, scale);
		
		// Draw resource rate
		String rateTxtHeader = "Fuel Rate:";
		String rateTxt = String.format("%.3g%n/s", fuelPushRate);
		gh.setFont(new Font("Serif", Font.PLAIN, 20));
		FontRenderContext frc = gh.getFontRenderContext();
		Rectangle2D rect = gh.getFont().getStringBounds(rateTxt, frc);
		Rectangle2D rectHeader = gh.getFont().getStringBounds(rateTxtHeader, frc);
		int xPosHeader = (int) (x - rectHeader.getWidth()/2);
		int yPosHeader = (int) (y);
		int xPos = (int) (x - rect.getWidth()/2);
		int yPos = (int) (y + rectHeader.getHeight());
		gh.setColor(new Color(0.0f,0.0f,0.0f,0.60f));
		int textWidth = (int) Math.max(rect.getWidth(), rectHeader.getWidth());
		gh.fillRoundRect(x-(int)(textWidth/2) - 3, yPosHeader - 3 - (int)rectHeader.getHeight(), (int)textWidth+6, (int)rect.getHeight()+(int)rectHeader.getHeight()+6, 8, 8);
		gh.setColor(Color.WHITE);
		gh.drawChars(rateTxt.toCharArray(), 0, rateTxt.length(), xPos, yPos-3);
		gh.drawChars(rateTxtHeader.toCharArray(), 0, rateTxtHeader.length(), xPosHeader, yPosHeader-3);
	}
	
	// Function needed in each item class for collision checking
	public static int checkCollision(Item other, Boolean notify) {
		int num = 0;
	    for(Iterator it = idList.iterator(); it.hasNext();) {
	    	Item idOn = (Item)it.next();
			if (idOn != other) {
				if (Item.intersection(other, idOn)) {
					if (notify) {
						idOn.collision(other);
					}
					num++;
				}
			}
		}
		return num;
	}
	
	// Returns the Item on the other end of the Wormhole currently being constructed if Nebula
	public static Item getWormholeEnd(){ // return handle to this object if the end of the wormhole is within its bounds
		Item returnItem = null;
		for(int count=0;count<idList.size();count++){
			Item tempItem = idList.get(count);
			if (Math.sqrt(Math.pow(tempItem.x-ViewControl.mouseX(),2)+Math.pow(tempItem.y-ViewControl.mouseY(),2)) < tempItem.r){ // determine if mouse is within object bounds
				returnItem = idList.get(count);
				break;
			}
			
		}
		return returnItem;
	}
	
	// Connects a Wormhole to self and also clears Fog around area
	public void addWormhole(Wormhole wid) {
		if (whList.isEmpty()) {
			Fog.uncover(x, y, 300);
		}
		whList.add(wid);
	}
	
	// Deletes a Wormhole from list
	public void removeWormhole(Wormhole wid){
		for(int count=0; count < whList.size(); count++){
			Wormhole nextHole = (Wormhole)whList.get(count);
			if(nextHole == wid){
				whList.remove(count);
				break;
			}
		}
	}
	
	// Called every second: is used to push fuel to connected planets
	public static void secondPulse(){
		for(Iterator it = idList.iterator(); it.hasNext();){
			Nebula idOn = (Nebula)it.next();
	    	int planetCount = 0;
	    	float fuelDistribution = 0;
	    	LinkedList<Planet> planetList = new LinkedList<Planet>();
	    	for(Iterator it2 = idOn.whList.iterator(); it2.hasNext();){
	    		Wormhole tempWormhole = (Wormhole)it2.next();
	    		Planet tempPlanet = tempWormhole.getOtherPlanet(idOn);
	    		if(tempPlanet == null){
	    			continue;
	    		}
	    		if(tempPlanet.maxFuel > tempPlanet.fuel && tempWormhole.active){
	    			planetCount++;
	    			planetList.add(tempPlanet);
	    		}
	    	}
    		if(planetCount > 0){
    			fuelDistribution = idOn.fuelPushRate/(float)planetCount;
    			Boolean allDistributed = false;
    			Boolean allFull = false;
    			float fuelLeft = idOn.fuelPushRate;
    			while(!allDistributed && !allFull){
    				allFull = true;
    				allDistributed = true;
	    			for(Iterator it3 = planetList.iterator(); it3.hasNext();){
	    				Planet pushPlanet = (Planet)it3.next();
	    				if(fuelLeft == 0){
	    					break;
	    				}
	    				else if(pushPlanet.fuel + fuelDistribution < pushPlanet.maxFuel){
	    					pushPlanet.fuel += fuelDistribution;
	    					fuelLeft -= fuelDistribution;
	    					allFull = false;
	    					allDistributed = false;
	    				}
	    				else if(pushPlanet.fuel == pushPlanet.maxFuel){
	    					continue;
	    				}
	    				else if(pushPlanet.fuel + fuelDistribution > pushPlanet.maxFuel){
	    					float tempFuel = pushPlanet.maxFuel - pushPlanet.fuel;
	    					pushPlanet.fuel = pushPlanet.maxFuel;
	    					fuelLeft -= tempFuel;
	    					allFull = false;
	    					allDistributed = false;
	    				}
	    				if(fuelLeft <= 0){
	    					allDistributed = true;
	    				}
	    			}
    			}
    		}
		}
	}
}
